/////////////////////////////////////////////////////
// File Name: weight_rr_arbiter.v
// Author: zeping fan
// mail:   zpfan007@163.com
// Created Time: 2023年09月22日 星期五 15时46分26秒
/////////////////////////////////////////////////////

module weight_rr_arbiter(

clk,
rst_n,
req,
weight,
grant,
grant_vld,
grant_ff,
grant_ff_vld,
switch_to_next
);

parameter   REQ_CNT         = 4;
parameter   GRANT_WIDTH     = 5;
parameter   INIT_GRANT      = {{REQ_CNT-1{1'b0}}, 1'b1};
parameter   WEIGHT_WIDTH    = GRANT_WIDTH * REQ_CNT;

input                       clk;
input                       rst_n;
input   [REQ_CNT-1:0]       req;
input   [WEIGHT_WIDTH-1:0]  weight;
input                       switch_to_next;
output  [REQ_CNT-1:0]       grant;
output                      grant_vld;
output  [REQ_CNT-1:0]       grant_ff;
output                      grant_ff_vld;

wire    [REQ_CNT-1:0]       grant;
wire                        grant_vld;
reg     [REQ_CNT-1:0]       grant_ff;
reg                         grant_ff_vld;

wire                        no_req;
wire                        no_grant;
wire                        first_grant;
wire                        arb_trig;

reg     [GRANT_WIDTH-1:0]   priority_cnt    [REQ_CNT-1:0];
wire    [REQ_CNT-1:0]       cnt_over;
wire                        round_en;

assign no_req       = ~(|req);
assign no_grant     = ~(|grant_ff);
assign first_grant  = ~no_req && no_grant;
assign arb_trig     = first_grant || switch_to_next;
assign round_en     = |cnt_over[REQ_CNT-1:0];


wire    [REQ_CNT-1:0]   req_masked;
wire    [REQ_CNT-1:0]   mask_higher_pri_reqs;
wire    [REQ_CNT-1:0]   grant_masked;
wire    [REQ_CNT-1:0]   unmask_higher_pri_reqs;
wire    [REQ_CNT-1:0]   grant_unmasked;
wire                    no_req_masked;
reg     [REQ_CNT-1:0]   mask_next;

//Simple priority arbitration for masked portion
assign req_masked[REQ_CNT-1:0] = req & mask_next;
assign mask_higher_pri_reqs[0] = 1'b0;
assign mask_higher_pri_reqs[REQ_CNT-1:1] = req_masked[REQ_CNT-2:0] | mask_higher_pri_reqs[REQ_CNT-2:0];
assign grant_masked[REQ_CNT-1:0] = req_masked[REQ_CNT-1:0] & ~mask_higher_pri_reqs[REQ_CNT-1:0];

//Simple priority arbitration for unmasked portion
assign unmask_higher_pri_reqs[0] = 1'b0;
assign unmask_higher_pri_reqs[REQ_CNT-1:1] = req[REQ_CNT-2:0] | unmask_higher_pri_reqs[REQ_CNT-2:0];
assign grant_unmasked[REQ_CNT-1:0] = req[REQ_CNT-1:0] & ~unmask_higher_pri_reqs[REQ_CNT-1:0];

//Use grant_masked if there is any there, otherwise use grant_unmasked
assign no_req_masked = ~(|req_masked);
assign grant = ({REQ_CNT{no_req_masked}} & grant_unmasked) | grant_masked;
assign grant_vld = (arb_trig && |req)? 1'b1 : 1'b0;

//round cnt 
generate 
genvar i;
for(i=0;i<REQ_CNT;i=i+1)begin
    
assign  cnt_over[i] = (priority_cnt[i] == weight[GRANT_WIDTH*(i+1)-1-:GRANT_WIDTH]-1'b1);

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        priority_cnt[i] <= {GRANT_WIDTH{1'b0}};
    else if(cnt_over[i])
        priority_cnt[i] <= {GRANT_WIDTH{1'b0}};
    else if(grant[i] && grant_vld)
        priority_cnt[i] <= priority_cnt[i] + 1'b1;
end
end
endgenerate

//pointer update
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        mask_next <= {REQ_CNT{1'b1}};
    else begin
        case({first_grant,round_en})
            2'b10:begin
                if(|req_masked)
                    mask_next <= req_masked | mask_higher_pri_reqs;
                else 
                    mask_next <= req | unmask_higher_pri_reqs;
            end
            2'b01,2'b11:begin
                if(|req_masked)
                    mask_next <= mask_higher_pri_reqs;
                else begin
                    if(|req)
                        mask_next <= unmask_higher_pri_reqs;
                    else 
                        mask_next <= mask_next;
                end
            end
            default:mask_next <= mask_next;
        endcase
    end
end


always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin 
        grant_ff <= {REQ_CNT{1'b0}};  
        grant_ff_vld <= 1'b0;
    end 
    else if(arb_trig)begin
        grant_ff <= grant;
        grant_ff_vld <= no_req? 1'b0 : 1'b1;
    end 
    else begin 
        grant_ff <= grant_ff;
        grant_ff_vld <= grant_ff_vld;
    end
end

endmodule



/*
module weight_rr_arbiter(

clk,
rst_n,
req,
weight,
grant,
grant_vld,
grant_ff,
grant_ff_vld,
switch_to_next
);


parameter   REQ_CNT         = 4;
parameter   GRANT_WIDTH     = 5;
parameter   INIT_GRANT      = {{REQ_CNT-1{1'b0}}, 1'b1};
parameter   WEIGHT_WIDTH    = GRANT_WIDTH * REQ_CNT;


input                       clk;
input                       rst_n;
input   [REQ_CNT-1:0]       req;
input   [WEIGHT_WIDTH-1:0]  weight;
input                       switch_to_next;
output  [REQ_CNT-1:0]       grant;
output                      grant_vld;
output  [REQ_CNT-1:0]       grant_ff;
output                      grant_ff_vld;



reg     [REQ_CNT-1:0]       grant_base;
wire    [REQ_CNT*2-1:0]     double_req;
wire    [REQ_CNT*2-1:0]     double_grant;

wire    [REQ_CNT-1:0]       grant;
wire                        grant_vld;
reg     [REQ_CNT-1:0]       grant_ff;
reg                         grant_ff_vld;


reg     [GRANT_WIDTH-1:0]   priority_cnt    [REQ_CNT-1:0];
wire    [REQ_CNT-1:0]       cnt_over;
wire    [REQ_CNT-1:0]       round_en;
wire                        req_change;

wire                        no_req;
wire                        no_grant;
wire                        first_grant;
wire                        arb_trig;

assign no_req       = ~(|req);
assign no_grant     = ~(|grant_ff);
assign first_grant  = no_grant && ~no_req; 
assign arb_trig     = first_grant || switch_to_next; 
assign req_change   = ~(|(grant_ff & req)) && |req;

generate 
genvar i;
for(i=0;i<REQ_CNT;i=i+1)begin
    
    assign  cnt_over[i] = priority_cnt[i] == weight[GRANT_WIDTH*(i+1)-1-:GRANT_WIDTH];
    assign  round_en[i] = cnt_over[i] || (priority_cnt[i]!=0 && req_change);

    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)
            priority_cnt[i] <= {GRANT_WIDTH{1'b0}};
        else if(cnt_over[i] && arb_trig)
            priority_cnt[i] <= {GRANT_WIDTH{1'b0}};
        else if(grant[i])
            priority_cnt[i] <= priority_cnt[i] + 1'b1;
    end
end
endgenerate

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        grant_base[REQ_CNT-1:0] <= INIT_GRANT;
    else if(|round_en && ~no_grant)
        grant_base[REQ_CNT-1:0] <= {grant_ff[REQ_CNT-2:0],grant_ff[REQ_CNT-1]};
end

assign double_req[REQ_CNT*2-1:0] = {req,req};
assign double_grant[REQ_CNT*2-1:0] = double_req & (~(double_req - grant_base));
assign grant = arb_trig? (double_grant[REQ_CNT*2-1:REQ_CNT] | double_grant[REQ_CNT-1:0]) : {REQ_CNT{1'b0}};
assign grant_vld = (arb_trig && ~no_req)? 1'b1 : 1'b0;

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        grant_ff <= {REQ_CNT{1'b0}};        
    else if(arb_trig)
        grant_ff <= grant;
    else 
        grant_ff <= grant_ff;
end

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        grant_ff_vld <= 1'b0;
    else if(arb_trig)
        grant_ff_vld <= no_req? 1'b0 : 1'b1;
end

endmodule
*/
